package com.t360.cubesolver;

import java.util.HashMap;

/**
 * It models the "solution" object
 * @author fmanganiello
 */
public class Solution implements Cloneable {
	/**
	 * Possible positions of a piece in the solution
	 * @author fmanganiello
	 */
	public enum Position {
		TOP_LEFT,
		TOP_MID,
		TOP_RIGHT,
		BOTTOM_TOP,
		BOTTOM_MID,
		BOTTOM_BOTTOM
	};
	
	private HashMap<Position, PuzzlePiece> solution;

	public Solution() {
		solution = new HashMap<Position, PuzzlePiece>();
	}
	
	/**
	 * A solution is an object that associates to each position a puzzle piece
	 * @param solution
	 */
	public Solution(HashMap<Position, PuzzlePiece> solution) {
		this();
		for (Position position : solution.keySet()) {
			this.solution.put(position, solution.get(position).clone());
		}
	}
	
	@Override
	public String toString() {
		StringBuffer out = new StringBuffer();
		if (!solution.containsKey(Position.TOP_LEFT) || solution.get(Position.TOP_LEFT) == null) {
			return "";
		}
		
		out.append(solution.get(Position.TOP_LEFT).align(
			solution.get(Position.TOP_MID),
			solution.get(Position.TOP_RIGHT)));
		
		if (solution.containsKey(Position.BOTTOM_TOP)) {
			out.append(solution.get(Position.BOTTOM_TOP).toIndentedString());
		}
		
		if (solution.containsKey(Position.BOTTOM_MID)) {
			out.replace(out.length()-2, out.length()-1, "");
			out.append(solution.get(Position.BOTTOM_MID).toIndentedString());
		}
		
		if (solution.containsKey(Position.BOTTOM_BOTTOM)) {
			out.replace(out.length()-2, out.length()-1, "");
			out.append(solution.get(Position.BOTTOM_BOTTOM).toIndentedString());
		}
		
		return out.toString();
	}
	
	/**
	 * Get the piece in a certain position
	 * @param pos
	 * @return
	 */
	public PuzzlePiece get(Position pos) {
		return solution.containsKey(pos) ? solution.get(pos) : null;
	}
	
	/**
	 * Set a piece in a certain position
	 * @param pos
	 * @param piece
	 */
	public void set(Position pos, PuzzlePiece piece) {
		solution.put(pos, piece);
	}
	
	/**
	 * Removes a piece (it searches for it first, if it does not exist, it does nothing)
	 * @param piece
	 */
	public void remove(final PuzzlePiece piece) {
		for (Position position : Position.values()) {
			if (solution.containsKey(position) && solution.get(position).index() == piece.index()) {
				solution.remove(position);
			}
		}
	}
	
	/**
	 * Verify whether the solution is valid, i.e. all the constraints are respected
	 * and it can be "closed" to form a cube without holes
	 * @return
	 */
	public boolean isValid() {
		// TOP_LEFT right side should match TOP_MID left side
		if (solution.containsKey(Position.TOP_MID) &&
				!solution.get(Position.TOP_LEFT).matchesRightOnLeftOfPiece(solution.get(Position.TOP_MID))) {
			return false;
		}
		
		// TOP_MID right side should match TOP_RIGHT left side
		if (solution.containsKey(Position.TOP_RIGHT) &&
				!solution.get(Position.TOP_MID).matchesRightOnLeftOfPiece(solution.get(Position.TOP_RIGHT))) {
			return false;
		}
		
		// TOP_MID bottom side should match BOTTOM_TOP top side
		// TOP_LEFT bottom side should match BOTTOM_TOP left side
		// TOP_RIGHT bottom side should match BOTTOM_TOP right side
		if (solution.containsKey(Position.BOTTOM_TOP) &&
				(!solution.get(Position.TOP_MID).matchesBottomOnTopOfPiece(solution.get(Position.BOTTOM_TOP))
					|| !solution.get(Position.TOP_LEFT).matchesBottomOnLeftOfPiece(solution.get(Position.BOTTOM_TOP))
					|| !solution.get(Position.TOP_RIGHT).matchesBottomOnRightOfPiece(solution.get(Position.BOTTOM_TOP)))) {
			return false;
		}
		
		// BOTTOM_TOP bottom side should match BOTTOM_MID top side
		// BOTTOM_MID left side should match TOP_LEFT left side
		// BOTTOM_MID right side should match TOP_RIGHT right side
		if (solution.containsKey(Position.BOTTOM_MID) &&
				(!solution.get(Position.BOTTOM_TOP).matchesBottomOnTopOfPiece(solution.get(Position.BOTTOM_MID))
					|| !solution.get(Position.BOTTOM_MID).matchesLeftOnLeftOfPiece(solution.get(Position.TOP_LEFT))
					|| !solution.get(Position.BOTTOM_MID).matchesRightOnRightOfPiece(solution.get(Position.TOP_RIGHT)))) {
			return false;
		}
		
		// BOTTOM_MID bottom side should match BOTTOM_BOTTOM top side
		// TOP_LEFT top side should match BOTTOM_BOTTOM left side
		// TOP_RIGHT top side should match BOTTOM_BOTTOM right side
		// BOTTOM_BOTTOM bottom side should match TOP_MID top side
		if (solution.containsKey(Position.BOTTOM_BOTTOM) &&
				(!solution.get(Position.BOTTOM_MID).matchesBottomOnTopOfPiece(solution.get(Position.BOTTOM_BOTTOM))
					|| !solution.get(Position.TOP_LEFT).matchesTopOnLeftOfPiece(solution.get(Position.BOTTOM_BOTTOM))
					|| !solution.get(Position.TOP_RIGHT).matchesTopOnRightOfPiece(solution.get(Position.BOTTOM_BOTTOM))
					|| !solution.get(Position.BOTTOM_BOTTOM).matchesBottomOnTopOfPiece(solution.get(Position.TOP_MID)))) {
			return false;
		}
		
		return true;
	}
	
	@Override
	public Solution clone() {
		return new Solution(solution);
	}
	
	/**
	 * Verify whether this solution already contains a certain piece
	 * @param piece
	 * @return
	 */
	public boolean contains(PuzzlePiece piece) {
		for (Position position : solution.keySet()) {
			if (solution.containsKey(position)) {
				PuzzlePiece tmpPiece = solution.get(position);
				if (tmpPiece != null && piece.index() == tmpPiece.index()) {
					return true;
				}
			}
		}
		
		return false;
	}
}
